using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace Framework
{
    public class CoroutineID
    {
        private object _author;
        public CoroutineID(object author)
        {
            _author = author;
        }
        public bool Finished { get; private set; }
        public void SetFinish(object author)
        {
            if (_author != author)
            {
                return;
            }
            Finished = true;
        }
    }
    public interface ICoroutineManager
    {
        CoroutineID Start(IEnumerator value, string tag);
        void Stop(CoroutineID id);
    }
    public class CoroutineManager : ICoroutineManager
    {
        public bool Enabled { get; private set; }
        private Transform transform;
        private List<CoroutineBehaviour> workList;
        private Framework.Core.CustomPool<CoroutineBehaviour> _pool;
        private int m_Index;
        private CoroutineBehaviour current;
        private int maxCount;
        public CoroutineManager(Transform parent = null)
        {
            maxCount = 50;
            workList = new List<CoroutineBehaviour>();
            _pool = new Core.CustomPool<CoroutineBehaviour>(() => { return createMember(); });
            Initialize(parent);
        }
        public void Initialize(Transform parent)
        {
            if (transform != null)
            {
                FrameworkLog.LogError("has Initialized");
                return;
            }
            if (parent == null)
            {
                FrameworkLog.LogError("协程需要找定一个父节点");
                return;
            }
            this.transform = parent;
            var behaviour = this.transform.gameObject.AddComponent<CoroutineManagerBehaviour>();
            if (behaviour == null)
            {
                FrameworkLog.LogError("can't Initialize");
                return;
            }
            behaviour.SetUpdateCallback(update);
            Enabled = true;
        }
        public CoroutineID Start(IEnumerator value)
        {
            if (!Enabled) return null;
            if (transform == null || transform.gameObject == null)
            {
                this.Enabled = false;
                return null;
            }
            int count = 1000;
            while (true && count-- > 0)
            {
                var behavior = _pool.Get();
                if (behavior == null)
                {
                    return null;
                }
                if (behavior.gameObject == null) continue;
                behavior.Frame = Time.frameCount;
                behavior.Tag = null;
                behavior.InstanceId = 0;
                workList.Add(behavior);
                current = behavior;
                return behavior.Run(value);
            }
            return null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="instanceId">实例id 可实现同时执行一个</param>
        /// <returns></returns>
        public CoroutineID Start(IEnumerator value, int instanceId)
        {
            var id = Start(value);
            if (id != null && instanceId != 0)
            {
                var len = workList.Count;
                while (len-- > 0)
                {
                    var behavior = workList[len];
                    if (behavior.InstanceId == instanceId)
                    {
                        Stop(behavior.ID); break;
                    }
                }
                current.InstanceId = instanceId;
            }
            return id;
        }
        public CoroutineID Start(IEnumerator value, string tag)
        {
            var id = Start(value);
            if (id != null)
            {
                if (string.IsNullOrEmpty(tag))
                {
                    tag = string.Empty;
#if UNITY_EDITOR
                    var info = Utils.Text.GetStackTraceInfo(1, 1);
                    if (info.Count > 0)
                    {
                        tag = info[0];
                    }
#endif
                }
                current.Tag = tag;
            }
            return id;
        }
        public void Stop(CoroutineID id)
        {
            if (id == null) return;
            int len;
            len = workList.Count;
            while (len-- > 0)
            {
                var behavior = workList[len];
                if (behavior.ID == id)
                {
                    behavior.Stop();
                    return;
                }
            }
        }
        public void Clear()
        {

        }
        private void update()
        {
            var len = workList.Count;
            while (len-- > 0)
            {
                var item = workList[len];
                if (item.Finished)
                {
                    workList.RemoveAt(len);
                    _pool.Release(item);
                }
            }
        }
        private CoroutineBehaviour createMember()
        {
            m_Index += 1;
            if (m_Index == maxCount)
            {
                Debug.LogError("请检查协程数量 已达到" + maxCount);
                maxCount += 50;
            }
            GameObject gameObject = new GameObject();
            gameObject.transform.SetParent(transform);
            var behavior = gameObject.AddComponent<CoroutineBehaviour>();
            behavior.SetId(m_Index);
            return behavior;
        }


        [DisallowMultipleComponent]
        internal class CoroutineManagerBehaviour : MonoBehaviour
        {
            private Action TickCallback;
            private void Update()
            {
                if (TickCallback != null)
                {
                    TickCallback.Invoke();
                }
            }
            public void SetUpdateCallback(Action value)
            {
                TickCallback = value;
            }
            private void OnApplicationQuit()
            {
                TickCallback = null;
            }
            private void OnDisable()
            {
                if (TickCallback == null) return;
                FrameworkLog.LogErrorFormat("协程管理器节点{0}不可用", this.name);
            }
        }


        [DisallowMultipleComponent]
        internal class CoroutineBehaviour : MonoBehaviour
        {
            private static int _tagId = 0;
            public string Tag;
            public int Frame;
            public int InstanceId;
            private int tagId;
            public CoroutineID ID { get; private set; }
            private const string WORKING = "{0} {1}";
            private const string DONE = "Coroutine";
            public bool Finished { get; private set; }
            // public int ID { get { return _id; } }
            private string _name;
            private int _id = -1;
            private IEnumerator _value;
            private bool _paused;
            private bool _enabled;
            private object _author;
            public void SetId(int value)
            {
                if (_id < 0)
                {
                    _id = value;
                    _name = string.Format("worker {0}", value.ToString("00"));
                }
                this.name = _name;
                _enabled = this.gameObject.activeInHierarchy;
            }
            private void OnEnable()
            {
                if (_paused)
                {
                    _paused = false;
                    StopAllCoroutines();
                    StartCoroutine(run());
                }
            }
            private void OnDisable()
            {
                if (!Finished)
                {
                    _paused = true;
                }
            }
            public CoroutineID Run(IEnumerator value)
            {
                tagId = _tagId++;
#if UNITY_EDITOR

                this.name = string.Format(WORKING, tagId, Tag);
#endif

                _value = value;
                Finished = false;
                _author = this;
                this.ID = new CoroutineID(_author);
                if (_enabled)
                {
                    StartCoroutine(run());
                }
                return this.ID;
            }
            public bool Equals(IEnumerator other)
            {
                return _value == other;
            }
            public void Stop()
            {
                StopAllCoroutines();
                finish();
            }
            private IEnumerator run()
            {
                yield return _value;
                finish();
            }
            private void finish()
            {
                Finished = true;
                _paused = false;
                this.ID.SetFinish(_author);
                this.ID = null;
                InstanceId = 0;
                _value = null;
#if UNITY_EDITOR
                this.name = string.Format(DONE, tagId, Tag);
#endif
            }
        }
    }


}

